package com.wk.paradise.redis.config.redisConfig;

import java.net.URI;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.BinaryJedis;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.exceptions.InvalidURIException;
import redis.clients.jedis.util.JedisURIHelper;

/**
 * @Classname RedisFactory
 * @Description redis工厂。
 * PooledObjectFactory 要定义个实例的声明周期
 * @Date 2019/12/18 10:34
 * @Author by weikai
 */
public class RedisFactory implements PooledObjectFactory<Jedis> {

  private Logger logger = LoggerFactory.getLogger(RedisFactory.class);

  private final AtomicReference<HostAndPort> hostAndPortAtomicReference = new AtomicReference<HostAndPort>();

  private final int timeout;

  private final String password;

  private final int database;

  private final String clientName;

  public RedisFactory(final String host, final int port, final int timeout, final String password, final int database,
      final String clientName) {
    this.hostAndPortAtomicReference.set(new HostAndPort(host,port));
    this.timeout = timeout;
    this.password = password;
    this.database = database;
    this.clientName = clientName;
  }

  public RedisFactory(final String host, final int port, final int timeout, final String password, final int database) {
    this(host, port, timeout, password, database, null);
  }

  public RedisFactory(final URI uri, final int timeout, final String clientName) {
    if (!JedisURIHelper.isValid(uri)) {
      throw new InvalidURIException(String.format("Cannot open Redis connection due invalid URI. %s", uri.toString()));
    }

    this.hostAndPortAtomicReference.set(new HostAndPort(uri.getHost(), uri.getPort()));
    this.timeout = timeout;
    this.password = JedisURIHelper.getPassword(uri);
    this.database = JedisURIHelper.getDBIndex(uri);
    this.clientName = clientName;

  }

  public void setHostAndPortAtomicReference(final HostAndPort hostAndPort){
    this.hostAndPortAtomicReference.set(hostAndPort);
  }

  /**
   * 新建一个实例时，会调用这个方法
   * @return
   * @throws Exception
   */
  @Override
  public PooledObject<Jedis> makeObject() throws Exception {
    final HostAndPort hostAndPort = this.hostAndPortAtomicReference.get();
    /*
    新建一个jedis对象
     */
    final Jedis jedis = new Jedis(hostAndPort.getHost(), hostAndPort.getPort(), this.timeout);

    jedis.connect();
    /*
    如果设置了密码，则需要密码认证
     */
    if (null != this.password) {
      jedis.auth(this.password);
    }
    /*
    如果设置了数据库，则需要设置一下数据库
     */
    if (database != 0) {
      jedis.select(database);
    }
    if (clientName != null) {
      jedis.clientSetname(clientName);
    }

    return new DefaultPooledObject(jedis);
  }

  /**
   * 当连接池中drop实例时，调用的方法
   * @param pooledJedis
   * @throws Exception
   */
  @Override
  public void destroyObject(PooledObject<Jedis> pooledJedis) throws Exception {
    final BinaryJedis jedis = pooledJedis.getObject();
    if (jedis.isConnected()) {
      try {
        jedis.quit();
      } catch (Exception e) {
        logger.warn("jedis关闭时出错", e);
      }
      try {
        jedis.disconnect();
      } catch (Exception e) {
        logger.warn("", e);
      }
    }
  }

  /**
   * 验证jedis的可用性
   * @param pooledJedis
   * @return
   */
  @Override
  public boolean validateObject(PooledObject<Jedis> pooledJedis) {
    final BinaryJedis jedis = pooledJedis.getObject();
    try {
      HostAndPort hostAndPort = this.hostAndPortAtomicReference.get();

      String connectionHost = jedis.getClient().getHost();
      int connectionPort = jedis.getClient().getPort();

      return hostAndPort.getHost().equals(connectionHost) && hostAndPort.getPort() == connectionPort && jedis.isConnected()
          && jedis.ping().equals("PONG");
    } catch (final Exception e) {
      return false;
    }
  }

  @Override
  public void activateObject(PooledObject<Jedis> pooledJedis) throws Exception {
    final BinaryJedis jedis = pooledJedis.getObject();
    if (jedis.getDB() != database) {
      jedis.select(database);
    }
  }

  @Override
  public void passivateObject(PooledObject<Jedis> p) throws Exception {

  }
}
